菜单国际化:正确使用i18n国际化的3种姿势
前置准备:路由元信息中的 title 配置
要让左侧菜单支持中英文切换,首先需要在所有页面路由中配置 title 元信息。无论是通过文件路由自动生成的页面,还是手动配置的路由,都需要在 definePageMeta 中设置 title 字段。这个字段将作为 i18n 翻译的 key,最终由菜单组件读取并显示。
// pages/components/icon/list.vue
definePageMeta({
title: 'pages.iconList'
})
typescript
对于和文件夹同名的基础组件(如 components/icon.vue),还需要额外设置 routerView 属性,否则页面不会正确渲染嵌套内容。
i18n 的三种使用姿势
在 Vue 3 项目中使用 vue-i18n 进行国际化时,不同上下文环境需要采用不同的调用方式。这是很多开发者容易混淆的地方。
方式一:模板中使用 $t
这是最直接的方式。在 <template> 中,vue-i18n 会自动注入 $t 全局方法,可以直接在模板表达式中调用。
<template>
<!-- 在 props 绑定中使用 $t -->
<el-menu-item :label="$t('component.iconList')">
图标列表
</el-menu-item>
</template>
vue
借助 VS Code 的 i18n Ally 插件,可以直接选中需要国际化的文本,按下 Cmd+. (macOS) 或 Ctrl+. (Windows) 快捷键,插件会自动提取文本并替换为 $t() 调用。在弹出的替换选项中,如果是在 props 绑定中(如 :label),应选择带冒号绑定的选项。
方式二:<script setup> 中使用 useI18n
在 <script setup> 部分,$t 不可用。必须从 vue-i18n 导入 useI18n 组合式函数,然后解构出 t 方法。
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const handleCopy = () => {
ElMessage.success(t('message.copySuccess'))
}
</script>
vue
同样可以使用 i18n Ally 插件来辅助:选中文本后按 Cmd+.,在弹出的替换选项中选择 useI18n 对应的选项(通常是倒数第二个),插件会自动完成导入和解构。
方式三:defineProps / definePageMeta 中使用字符串 key
这是一个特殊场景。definePageMeta 和 defineProps 是编译器宏,它们在 setup 函数之外初始化,因此无法使用任何 Composition API(包括 useI18n)。在这种上下文中,只能直接使用 i18n 的 key 字符串:
// 只能使用字符串 key,不能使用 $t() 或 t()
definePageMeta({
title: 'pages.iconList'
})
defineProps({
title: {
type: String,
default: 'pages.iconList'
}
})
typescript
| 使用场景 | 调用方式 | 说明 |
|---|---|---|
<template> 中的文本和属性 | $t('key') | 全局注入,直接可用 |
<script setup> 中的逻辑代码 | const { t } = useI18n() | 需要手动导入和解构 |
defineProps / definePageMeta | 字符串 'key' | 编译器宏,无法使用 Composition API |
key 命名的最佳实践
对于 title 字段的 i18n key,建议采用与页面功能直接对应的命名方式。例如,图标列表页面的 title 设置为 pages.iconList,而不是用一个抽象的编号。这样做有两个好处:
- 语义清晰:看到 key 就能知道它对应的翻译内容
- 便于维护:在 locales 文件中查找翻译时能快速定位
// locales/zh-CN.json
{
"pages": {
"iconList": "图标列表",
"components": "组件示例",
"notice": "通知组件"
},
"component": {
"iconList": "图标列表"
}
}
json
菜单组件中的翻译适配
要让菜单在切换语言时自动更新显示文本,需要在 Menu 和 MenuItem 组件中对 meta.title 字段进行 $t() 包裹:
<!-- MenuItem.vue -->
<template>
<el-menu-item :index="item.meta?.key">
<!-- 使用 $t 包裹 title,并提供默认值 -->
{{ $t(item.meta?.title || '') }}
</el-menu-item>
</template>
vue
<!-- SubMenu.vue -->
<template>
<el-sub-menu :index="item.meta?.key">
<template #title>
{{ $t(item.meta?.title || '') }}
</template>
<!-- 递归渲染子菜单 -->
</el-sub-menu>
</template>
vue
这里的关键点是为 $t() 提供一个默认值(空字符串),防止 meta.title 不存在时出现翻译 key 为 undefined 的错误。
关于 defineProps 中使用 t 函数的说明
有些开发者会在 defineProps 的默认值中写一个箭头函数形式的 t 调用:
defineProps({
title: {
type: String,
// 这里的 t 函数不是 useI18n 解构出来的
// 而是一个纯粹的 VS Code i18n Ally 插件兼容写法
default: () => t('pages.iconList')
}
})
typescript
这种写法的目的纯粹是为了让 VS Code 的 i18n Ally 插件能够在编辑器中直接显示翻译后的文本,而非显示一个 key 字符串。实际上 i18n 真正生效的位置是在模板中对 title 使用 $t() 包裹的地方。建议统一不在 defineProps 中使用 t 函数,全部采用字符串 key 的写法,保持一致性。
关键要点总结
- 三种 i18n 使用方式各有适用场景:模板用
$t,script 用useI18n,编译器宏中只能用字符串 key definePageMeta和defineProps中无法使用 Composition API,这是 Vue 编译器宏的设计限制- key 命名遵循语义化原则,让团队成员看到 key 就能联想到对应的翻译内容
- 菜单组件中需要对
meta.title进行$t()包裹,并提供安全的默认值
↑